/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
  * Name: mp_plugin_manager.cpp
  *
  * Purpose: plugin manager class implementation for micro player
  *
  * Developer:
  *   wen.gu , 2018-09-03
  *
  * TODO:
  *
  ***************************************************************************/


/******************************************************************************
**    INCLUDES
******************************************************************************/

#include "mp_shared_library.h"

#include "mp_plugin_manager.h"
#include "mp_plugin.h"


#define LOG_TAG "PluginManager"
#include "mp_log.h"

/******************************************************************************
**    MACROS
******************************************************************************/

/******************************************************************************
**    VARIABLE DEFINITIONS
******************************************************************************/



/******************************************************************************
**    FUNCTION DEFINITIONS
******************************************************************************/

eMPError MPPluginManager::load_l(const std::string& name)
{
    eMPError ret = eMPError::BadParameter;

    if (libIsLoaded(name) == false)
    {
         void* dlHd = MPSLOpen(name.c_str());
         

        if (dlHd)
        {
            pluginEntryFunc_t pluginEntry = 
                      (pluginEntryFunc_t)MPSLSym(dlHd, MP_PLUGIN_ENTRY_NAME);
            pluginHdrVerFunc_t verFunc = 
                  (pluginHdrVerFunc_t)MPSLSym(dlHd, MP_PLUGIN_VER_FUNC_NAME);

            if (pluginEntry && verFunc)
            {
                const std::string ver = verFunc();

                if (ver == MP_PLUGIN_HEADER_VER)
                {/** todo, process the compatibility of master version */
                    MPPlugin* mp = pluginEntry();

                    ret = eMPError::OK;
                    MPPluginHandle mph;
                    mph.dlHd = dlHd;
                    mph.name = name;
                    mLibMap[mp] = mph; 
                    LOGD("load plugin(%s, ver: %s, author: %s) success "
                         "with plugin interface ver:%s\n",
                         mp->name().c_str(), mp->version().c_str(),
                         mp->author().c_str(), MP_PLUGIN_HEADER_VER);
                }
                else
                {
                    LOGE("version mismatch, host(%s), plugin(%s)\n",
                        MP_PLUGIN_HEADER_VER, ver.c_str());
                }
            }
            else
            {
                LOGE("lib(%s) haven't entry func(%s, %s)\n",
                    name.c_str(), MP_PLUGIN_ENTRY_NAME, 
                    MP_PLUGIN_VER_FUNC_NAME);
                ret = eMPError::Undefined;
            }
        }  
        else
        {
            LOGE("load lib(%s) failed(%s)\n", name.c_str(), MPSLError());
        }
    }
    else
    {
        LOGE("lib(%s) already loaded\n", name.c_str());
    }
    
    return ret;
    
}

bool MPPluginManager::libIsLoaded(const std::string& name)
{
    bool ret= false;
    PluginLibraryMap_t::iterator it = mLibMap.begin();

    for (; it != mLibMap.end(); it++)
    {
        MPPluginHandle& mph = it->second;

        if (name == mph.name)
        {
            ret = true;
            break;
        }
    }

    return ret;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

MPPluginManager::~MPPluginManager()
{
    unloadAll();
    /**todo, something */
}

//static
MPPluginManager& MPPluginManager::getInstance()
{
    static MPPluginManager mpm;
    return mpm;
}


eMPError MPPluginManager::load(const std::string& name)
{
    //AutoLock al(mLock);
    return load_l(name);
}

eMPError MPPluginManager::loadList(std::vector<std::string>& pluginList, 
                                           bool ignoreError/* = true*/)
{
    eMPError ret = eMPError::OK;
    uint32_t loadCnt = 0;
    //AutoLock al(mLock);

    for (uint32_t i = 0; i < pluginList.size(); i++)
    {
        ret = load_l(pluginList[i]);

        if (eMPError::OK == ret)
        {
            loadCnt++;
        }
        else if (ignoreError)
        {
            ret = eMPError::OK;
        }
        else
        {
            LOGE("load plugin(%s) failed(0x%0x, %s)\n", 
                pluginList[i].c_str(), ret, MPError2Str(ret));
            break;   
        }
    }

    LOGD("loaded plugin(%d)\n", loadCnt);

    return ret;    
}


eMPError MPPluginManager::unloadAll()
{
    eMPError ret = eMPError::OK;
    //AutoLock al(mLock);

    PluginLibraryMap_t::iterator it = mLibMap.begin();

    for (; it != mLibMap.end(); it++)
    {
        MPPlugin* mp = it->first;
        MPPluginHandle& mph = it->second;    

        if (mp)
        {
            delete mp;
        }

        if (mph.dlHd)
        {
            MPSLClose(mph.dlHd);
        }
    }

    mLibMap.clear();

    return ret;
}

eMPError MPPluginManager::find(const MPCCreateParam* param, 
                              MPComponent** comp)
{
    eMPError ret = eMPError::NotFound;
    //AutoLock al(mLock);
    PluginLibraryMap_t::iterator it = mLibMap.begin();

    for (; it != mLibMap.end(); it++)
    {
        MPPlugin* mp = it->first;
        if (mp)
        {
            LOGD("===>(type: %s, plugin: %s)\n", 
                param->type.c_str(), mp->name().c_str());
                
            ret = mp->createComponent(param, comp);
            
            //LOGD("===>ret(%d, %s)\n", ret, MPError2Str(ret));
            if (eMPError::OK == ret)
            {
                LOGD("create component instance success, name: %s, version: %s\n",
                     (*comp)->name().c_str(), (*comp)->version().c_str());
                break;
            }
        }  
    }

    return ret;
}




